package dongchel.vrp.clonalselection1;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import dongchel.vrp.data.WriteData;
import dongchel.vrp.entity.Route;
import dongchel.vrp.entity.Solution;
import dongchel.vrp.general.CompareObject;
import dongchel.vrp.general.InitialSolution;
import dongchel.vrp.general.SimpleCompareObject;
import dongchel.vrp.localsearch.LocalSearch;
import dongchel.vrp.localsearch.Mutation;
import dongchel.vrp.utils.CollectionUtils;
import dongchel.vrp.view.ShowSolution;
import dongchel.vrp.view.TrendChart;

public class AIS {
	// for each iteration, we will get a set of random weights
	private double[] objectiveWeights;
	private double[] penaltyWeights;

	// initial solution generator
	private InitialSolution init = null;
	private List<Solution> population = null;

	private Fitness fitness;
	
	private WriteData writeData;

	//in order to print data trend, we need to define more variables
	private TrendChart trendChart;
	private List<Double> numberList;
	private List<Double> lengthList;
	private List<Double> timeViolationList;
	private List<Double> demandList;
	
	public void init() {
		this.objectiveWeights = new double[2];
		this.objectiveWeights[0] = 0.5;
		this.objectiveWeights[1] = 0.5;

		this.penaltyWeights = new double[2];
		this.penaltyWeights[0] = 0.6;
		this.penaltyWeights[1] = 0.4;
		
		init = new InitialSolution();
		population = init.genePopByAngle(100);
		fitness = new Fitness();
		fitness.setObjectiveWeights(this.objectiveWeights);
		fitness.setPenaltyWeights(this.penaltyWeights);
		
		writeData = new WriteData();
		writeData.begin();
		
		trendChart = new TrendChart();
	}

	public AIS() {
		init();
		
		
		
		/*for(int i=1;i<5;i++){
			for(int j=1;j<5;j++){
				for(int k=0;k<5;k++){
					fitness.setObjectiveWeight(0.5*i);
					fitness.setPenaltyWeight(0.5*j);
					fitness.setSpeedWeight(0.5*k);
					process();
				}
			}
		}
		
		
		fitness.setObjectiveWeight(1);
		fitness.setPenaltyWeight(1);
		fitness.setSpeedWeight(0.5);
		
		for(int i=1;i<6;i++){
			for(int j=1;j<6;j++){
				this.objectiveWeights[0] = 0.2*i;
				this.objectiveWeights[1] = 1-0.2*i;
				
				this.penaltyWeights[0] = 0.2*j;
				this.penaltyWeights[1] = 1-0.2*j;
				
				process();
			}
		}*/
		process();
		
		trendChart.showData();
		
		end();
	}
	
	
	private void process(){
		this.numberList = new ArrayList<Double>();
		this.lengthList = new ArrayList<Double>();
		this.timeViolationList = new ArrayList<Double>();
		this.demandList = new ArrayList<Double>();
		
		int iter = 0;
		while (iter < 1000) {
			
			/**
			 * set objective weights and penalty weights
			 */		
			System.out.println("iter:"+iter);
			iter++;
			fitness.setPopulation(population);
			fitness.calculateFitnessForPopulation();

			/**
			 * choose 5% best solutions as antigen
			 */
			List<Solution> newPopulation = new ArrayList<Solution>();
			for (int i = 0; i < population.size() * 0.05; i++) {
				Solution solution = population.get(fitness.getCompareList()
						.get(i).getPosition());
				
				//record value of first solution
				if(i == 0){
					this.numberList.add(solution.getObjectiveValue().getObjectiveList().get(1));
					this.lengthList.add(solution.getObjectiveValue().getObjectiveList().get(1));	
					this.timeViolationList.add(solution.getTimeViolation());
					this.demandList.add(solution.getCapacityViolation());
				}
				
				newPopulation.add(solution);
				for (int j = 0; j < 120; j++) {
					Solution s = Mutation.mutatedAndLocalSearch(solution);
					s.setLastObjective(solution.getObjectiveValue());
					s.setLastPenalty(solution.getPenaltyValue());
					newPopulation.add(s);
				}
			}
			
			fitness.setPopulation(newPopulation);
			fitness.calculateFitnessForPopulation();
			
			population = new ArrayList<Solution>();
			boolean controller = true;
		
			for(int i=0;i<fitness.getCompareList().size();i++){
				if(!controller){
					break;
				}else{
					Solution solution = newPopulation.get(fitness.getCompareList()
							.get(i).getPosition());
					if(solution.checkTimeFeasibility()&&solution.checkDemandFeasibility()){
						population.add(solution);
						if(population.size()>=100){
							controller = false;
						}
					}
				}			
			}
		
			for(int i=0;i<fitness.getCompareList().size();i++){
				if(!controller){
					break;
				}else{
					Solution solution = newPopulation.get(fitness.getCompareList()
							.get(i).getPosition());
					if(!solution.checkTimeFeasibility()||!solution.checkDemandFeasibility()){
						population.add(solution);
						if(population.size()>=100){
							controller = false;
						}
					}
				}
				
			}
			
		}
		
		Solution  solution = population.get(0);
		/*System.out.println(solution.getTimeViolation()+"  "+solution.getCapacityViolation());
		ShowSolution.showSolution(solution, 2);*/
		
		writeData.logSolution(this, solution);
		
		this.trendChart.addData(this.numberList, "route number");
		this.trendChart.addData(this.lengthList, "tour length");
		this.trendChart.addData(this.timeViolationList, "time violation");
		this.trendChart.addData(this.demandList, "capacity violation");
	}
	
	//close ais
	public void end(){
		writeData.end();
	}
	
	
	/**
	 * according to fitness value, elitism solutions are chosen
	 * @return
	 * 
	 */
	

	public double[] getObjectiveWeights() {
		return objectiveWeights;
	}

	public void setObjectiveWeights(double[] objectiveWeights) {
		this.objectiveWeights = objectiveWeights;
	}

	public double[] getPenaltyWeights() {
		return penaltyWeights;
	}

	public void setPenaltyWeights(double[] penaltyWeights) {
		this.penaltyWeights = penaltyWeights;
	}

	public InitialSolution getInit() {
		return init;
	}

	public void setInit(InitialSolution init) {
		this.init = init;
	}

	public List<Solution> getPopulation() {
		return population;
	}

	public void setPopulation(List<Solution> population) {
		this.population = population;
	}

	public Fitness getFitness() {
		return fitness;
	}

	public void setFitness(Fitness fitness) {
		this.fitness = fitness;
	}
}
